#include "cmaster.h"
#include "cremote.h"
#include "cclient.h"

#include <buola/io/csocketacceptor.h>
#include <buola/io/cipendpoint.h>
#include <buola/io/clocalendpoint.h>
#include <buola/io/csocket.h>
#include <buola/io/cactivationendpoint.h>
#include <buola/utility/uuid.h>
#include <buola/algorithm/container.h>

namespace buola { namespace bboxd {

CMaster::CMaster()
    :   mUUID(get_uuid())
{
    //we don't use packet-oriented sockets (SCTP) because they don't seem to work well between different networks
    //i.e. routers don't forward them properly and it is not supported in some platforms
//     io::CSocketAcceptor *lIPAcceptor=new io::CSocketAcceptor(io::CIPEndPoint(9595));
//     lIPAcceptor->AcceptAsync(std::bind(&CMaster::OnNewRemoteConnection,this,_1));
    mLocalAcceptor=io::listen_at(io::CAbstractLocalActivationEndPoint(0,"org.buola.bboxd"),
                                                      io::ESocketType::PACKET);
    mLocalAcceptor->AcceptAsync(std::bind(&CMaster::OnNewLocalConnection,this,_1));
//     io::CSocketAcceptor *lPeerAcceptor=new io::CSocketAcceptor(io::CIPEndPoint(9596));
//     lPeerAcceptor->AcceptAsync(std::bind(&CMaster::OnNewPeerConnection,this,_1));
}

CMaster::~CMaster()
{
}

void CMaster::RegisterClient(CClient* pClient)
{
    mClients.emplace(pClient->UID(),pClient);
}

void CMaster::UnregisterClient(CClient* pClient)
{
    associative_erase_if(mServices,[pClient](const std::pair<std::string,CService> &pI){return pI.second.Client()==pClient;});
    
    mClients.erase(pClient->UID());
}

CClient *CMaster::GetClient(uint64_t pUID)
{
    auto i=mClients.find(pUID);
    if(i==mClients.end())
        return nullptr;
    return i->second;
}

bool CMaster::RegisterService(const std::string& pAddress,CClient* pClient,uint64_t pUID)
{
    if(mServices.find(pAddress)!=mServices.end())
        return false;
    
    mServices.emplace(pAddress,CService(pClient,pUID));
    return true;
}

bool CMaster::UnregisterService(const std::string& pAddress,CClient* pClient)
{
    return false;
}

CService* CMaster::GetService(const std::string &pName)
{
    auto i=mServices.find(pName);
    if(i==mServices.end()) return nullptr;
    return &i->second;
}

#if 0
void CMaster::RegisterRemote(CRemote *pRemote)
{
    if(!pRemote->Host().empty())
        mRemotesByHost[pRemote->Host()]=pRemote;
    if(!pRemote->UUID().empty())
        mRemotesByUUID[pRemote->UUID()]=pRemote;
}

void CMaster::UnregisterRemote(CRemote *pRemote)
{
    if(!pRemote->Host().empty())
        mRemotesByHost.erase(pRemote->Host());
    if(!pRemote->UUID().empty())
        mRemotesByUUID.erase(pRemote->UUID());
}

CBBoxRemote *CMaster::RemoteByHost(const std::string &pHost)
{
//     std::map<std::string,CBBoxRemote*>::iterator lFind=mRemotesByHost.find(pHost);
// 
//     if(lFind!=mRemotesByHost.end()) return lFind->second;
//     }
// 
//     try
//     {
//         CBBoxRemote *lRemote=new CBBoxRemote(this,pHost);
//         lRemote->WaitHello();
//         return lRemote;
//     }
//     catch(std::exception &pE)
//     {
//         msg_error() << "caught exception in thread " << pE.what() << "\n";
//         return nullptr;
//     }

    return nullptr;
}

CBBoxRemote *CMaster::RemoteByUUID(const std::string &pUUID)
{
//     {
//         lock_guard<mutex> lLock(mRemoteMutex);
//         
//         std::map<std::string,CBBoxRemote*>::iterator lFind=mRemotesByUUID.find(pUUID);
//         
//         if(lFind!=mRemotesByUUID.end()) return lFind->second;
//     }
// 
    return nullptr;
}
#endif

#if 0
io::PSocket CMaster::GetPeerConnection(uint64_t pID)
{
    lock_guard<mutex> lLock(mPeerMutex);

    std::map<uint64_t,io::PSocket>::iterator lFind=mPeerConnections.find(pID);
    
    if(lFind!=mPeerConnections.end())
    {
        io::PSocket lRet=lFind->second;
        mPeerConnections.erase(lFind);
        return lRet;
    }

    return nullptr;
}
#endif

void CMaster::OnNewLocalConnection(io::PSocket pSocket)
{
    msg_info() << "new local connection\n";
    new CClient(this,std::move(pSocket));
}

#if 0
void CMaster::OnNewRemoteConnection(io::PSocket pSocket)
{
    new CRemote(this,std::move(pSocket));
}

void CMaster::OnNewPeerConnection(io::PSocket pSocket)
{
    lock_guard<mutex> lLock(mPeerMutex);

    uint64_t lUID=get_new_uid();
    mPeerConnections[lUID]=pSocket;
    pSocket->SendPacket(&lUID,sizeof(lUID));
}
#endif

/*namespace bboxd*/ } /*namespace buola*/ }
